#include "ppd.h"
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
#include "sock.h"
#include "socket.h"
#include "semaf.h"
#include <ctype.h>
#include "config.h"
#include "protocolo_NIPC.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "look.h"
#include "fscan.h"
#include <pthread.h>

#define MAX_EVENTOS 1

key_t claveSem;
int semConsolaPDD;
int cliente;
int epoll;
int socketServer;
int sock;
int socketConsola;
int invalid=1,invRaid=1;
int mutexColaEpoll;
struct sockaddr_in data2;
ptrColaEpoll epollColaInic = NULL;
ptrColaEpoll epollColaFin = NULL;

int main()
{
	printf("----------------------------------------------------------------------------\n");
	printf("******---------------- PROCESO PLANIFICADOR DE DISCO -----------------******\n");
	printf("********   Tp Operativos: Fuselage  --  2do Cuatrimestre  2011     *********\n");
	printf("----------------------------------------------------------------------------\n\n");
	abrirConfig();

	pedInic=NULL;
	pedFin=NULL;
	pedAcum=NULL;
	mutexSock=inicia(1,IPC_PRIVATE);
	//conectar con FS y/o RAID
	contadorDuplicado=0;
	abrirArchivoDisco();
	obtenerCHS();
	if(strcmp(algoritmo,"LOOK") ==0){
		cabezal_l.SectorLogico = 15;
		cabezal_l.ptrSgte=NULL;
	}
	else {
		cabezal_f.SectorLogico = 15;
		cabezal_f.flag=-1;
		cabezal_f.ptrSgte=NULL;
		ptrLstPrim_f = NULL;
		ptrLstSec_f = NULL;
		ptrLstAcum_f = NULL;
	}

	epoll= epoll_create(15);

	/*if(epoll<0) {
		printf("se cago el epoll\n");
	}*/

	sentidoBrazo = 'U';

	banderaControl = 0;
	separarFunciones();

	return 0;

}

int separarFunciones()
{
	int s1;
	struct epoll_event elEvento;
	s1=inicia(0, IPC_PRIVATE);
	mutexColaEpoll=inicia(1,IPC_PRIVATE);
	struct sockaddr_un elData;

	controlPedidoFS = 1;
	claveSem=ftok("/bin/ls",76);
	if(claveSem == (key_t)-1){
		printf("NO SE PUDO CREAR LA CLAVE.");
	}
	printf("clave pdd= %d\n",(int)claveSem);
	semConsolaPDD=inicia(1,claveSem);

	if(0!= fork()){
		/*Acá comienza el quilobo. creo el socket y
		 * de acuerdo con el modo me voy para el raid o para
		 * el FS
		 */
		char buffHandShake[1024];
		memset(buffHandShake,0,1024);


		ip[strlen(ip)]='\0';puerto[strlen(puerto)]='\0';
		data2.sin_family=AF_INET;
		data2.sin_addr.s_addr=inet_addr(ip);
		data2.sin_port=htons(atoi(puerto));
		if(strstr(modo,"LISTEN")!=NULL){
			iniciarSocketA(&sock);

			int yes = 1;
				int retcode;

				retcode = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
				if(retcode == -1)
				{
					perror("\033[31m## ERROR: setsockopt() - SO_REUSEADDR\033[0m");
					exit(1);
				}

			realizarBindA(sock,data2);

			//pongo el socket a la espera de conexiones

			realizarListenA(sock);
			cliente=0;
			cliente=aceptarClienteA(sock,data2);
			recibirDatosA(buffHandShake,cliente);
			int paylen=1;int id=1;
			memcpy(&paylen,buffHandShake+sizeof(uint8_t),sizeof(uint16_t));
			memcpy(&id,buffHandShake,sizeof(uint8_t));
			if((id==0)&&(paylen==0)){
				enviarDatosA(buffHandShake,3,cliente);
				invalid=0;
			}
			else{
				NIPC paquete1;

				char*rta="-1 handshake invalido. Payload size diferente de cero";
				char*rta2="-1 no puede realizar una operacion sin un handshake previo";
				if(paylen!=0){
					paquete1.mensaje=malloc(sizeof(char)*strlen(rta));
					cargarEstructura(&paquete1,0,rta);
				}
				else{
					paquete1.mensaje=malloc(sizeof(char)*strlen(rta2));
					cargarEstructura(&paquete1,0,rta2);
				}
					memset(buffHandShake,0,1024);
					serializar_NIPC(paquete1,buffHandShake);
					enviarNIPC(buffHandShake,3+ paquete1.payloadLength,cliente);
					free(paquete1.mensaje);
					memset(buffHandShake,0,1024);
					close(cliente);
					invalid = 1;
					printf("Fallo handshake con el FS. Sólo Modo consola\n");
			}

		}
		else{//modo connect

			NIPC paquete2;
			char buffrecv[5];
			memset(buffrecv,0,5);
			iniciarSocketA(&sock);

			conectarSocketA(sock,data2);
			paquete2.mensaje=malloc(strlen(IDDisco) + sizeof(int)*3);
			char *mensaje;
			mensaje = malloc(strlen(IDDisco) + sizeof(int)*3);
			sprintf(mensaje,"%d.%d.%d.%s",pista,cabeza,sector,IDDisco);
			cargarEstructura(&paquete2,0,mensaje);
			memset(buffHandShake,0,1024);
			serializar_NIPC(paquete2,buffHandShake);
			enviarNIPC(buffHandShake,3+ paquete2.payloadLength,sock);

			recibirDatosA(buffHandShake,sock);
			int paylen=0;
			int ID = 1;
			memcpy(&ID,buffHandShake,sizeof(uint8_t));
			memcpy(&paylen,buffHandShake+sizeof(uint8_t),sizeof(uint16_t));
			if(ID==0 && paylen ==0){
				invRaid=0;
				printf("conecte con RAID\n");
			}
			else{
				invRaid=1;
				printf("Fallo handshake con el RAID. Sólo Modo consola\n");
			}
			free(paquete2.mensaje);
			free(mensaje);
			memset(buffHandShake,0,1024);


		}

	    //levanto el server de sock linux. ya termine las conexiones
		//del raid o del fs para bien o para mal.

		socketServer = mainSocketServer(&elData);


	    //agrego los sockets que correspondan al epoll

		//elEvento=calloc (MAX_EVENTOS, sizeof elEvento);
		//elEvento=malloc(MAX_EVENTOS*sizeof(struct epoll_event));

		if(invalid==0||invRaid==0){
			//si al menos uno de los dos es cero
            //(ambos arrancan en uno. el handshake sólo modifica 1 de los dos
			//de acuerdo a si es listen o connect)

			elEvento.data.fd = cliente;
			elEvento.events = EPOLLIN | EPOLLET;
			epoll_ctl(epoll,EPOLL_CTL_ADD,cliente,&elEvento);
		}
		// arranco el epoll como siempre

		int a =0;
			if(a==0){
				up(s1);
				socketConsola =accept(socketServer,NULL,NULL);
				int flags;
				flags = fcntl(socketConsola, F_GETFL, 0);
				flags |= O_NONBLOCK;
				fcntl(socketConsola, F_SETFL, flags);
				a=1;
				pthread_t hiloEliminar;
				pthread_t hiloEpoll;
				pthread_t hiloConsola;
				pthread_create(&hiloConsola,NULL,&atenderConsola,NULL);
				pthread_create(&hiloEliminar,NULL,&eliminar,NULL);
				pthread_create(&hiloEpoll,NULL,&encolarEpoll,NULL);

			}
			//habilito la consola para esta vuelta


				uint8_t id;
				uint16_t tamanio = 0;
				char buffer[1024];
				int fd;
				while(1){
					if(epollColaInic!=NULL){
						memset(buffer,0,1024);
						down(mutexColaEpoll);
						fd = quitarColaEpoll(buffer);

						memcpy(&id,buffer,sizeof(uint8_t));
						memcpy(&tamanio,buffer+sizeof(uint8_t),sizeof(uint16_t));
						parametros auxiliar;
						auxiliar.cliente = fd;
						auxiliar.id = id;
						auxiliar.semaforo = semConsolaPDD;
						auxiliar.data=malloc(sizeof(uint8_t)*tamanio+1);
						memset(auxiliar.data,0,tamanio+1);
						memcpy(auxiliar.data,buffer+sizeof(uint8_t)+sizeof(uint16_t),tamanio*sizeof(uint8_t));
						auxiliar.data[tamanio] = '\0';
						memset(buffer,0,1024);

						switch(auxiliar.id){
							case 1: pedidoLectura(auxiliar);//leer (cuando respondes no me das numero de sector)
								break;
							case 2: pedidoLecturaD(auxiliar);//tenes q mandarme un payload vacio o un mensaje de error.
								break;
							case 3: pedidoEscritura(auxiliar);
								break;
							case 4: pedidoEscrituraD(auxiliar);
								break;
							case 5: comandoInfo(auxiliar);
								break;
							case 6: comandoClean(auxiliar);
								break;
							case 7: comandoTrace(auxiliar);
								break;
						}
						up(mutexColaEpoll);
					}
				}






	}
		else{
			down(s1);

		execl(path,"consolaPDD",NULL);

			}
return 0;
}

int obtenerCHS (void)
{
	char* vector;

	vector = strtok(chs," ");
	vector = strtok(chs,".");
	pista = atoi(vector);
	vector = strtok(NULL,".");
	cabeza = atoi(vector);
	vector = strtok(NULL,".");
	sector = atoi(vector);

	return 0;
}

void comandoInfo(parametros aux)
{
	NIPC paquete;
	char* bufferNIPC1;
	char mensaje[50];

	if(strcmp(algoritmo,"LOOK") ==0)
	sprintf(mensaje,"posicion cabezal: %d:%d\n",cabezal_l.SectorLogico/pista,cabezal_l.SectorLogico%pista);

	else
	sprintf(mensaje,"posicion cabezal: %d:%d\n",cabezal_f.SectorLogico/pista,cabezal_f.SectorLogico%pista);

	cargarEstructura(&paquete,1,mensaje);
	bufferNIPC1 = malloc(sizeof(char)* paquete.payloadLength + sizeof(uint8_t) + sizeof(uint16_t) );
	serializar_NIPC(paquete,bufferNIPC1);
	enviarNIPC(bufferNIPC1,paquete.payloadLength+3,aux.cliente);
	memset(mensaje,0,50);
	free(paquete.mensaje);
	free(bufferNIPC1);
}

void comandoClean(parametros aux)
{
	colaParam auxiliar;
	char *strSectInic,*strSectFin;
	int sectInic, sectFin, i;
	char buffer[512];
	memset(buffer,0,512);

	strtok(aux.data," ");//quita el "clean "
	strSectInic=strtok(NULL," ");
	strSectFin=strtok(NULL," ");

	sectInic=atoi(strSectInic);
	sectFin=atoi(strSectFin);

	if(sectInic >= 0 && sectFin <= (sector*pista-1)){
		memset(auxiliar.buffer,0,512);
		auxiliar.codigo = 6;
		auxiliar.flag = 1;
		auxiliar.socket = aux.cliente;
		auxiliar.sem = aux.semaforo;
		auxiliar.sgte = NULL;
		contadorClean = sectFin - sectInic + 1;
		comparacionClean = 0;

		if(strcmp(algoritmo,"LOOK") ==0){
			for(i=sectInic;i<sectFin+1;i++){
				auxiliar.SectorLogico = i;
				selecCola_look(&pedInic,&pedFin,auxiliar,sentidoBrazo);
			}
		}

		if(strcmp(algoritmo,"F-SCAN") ==0) {
			for(i=sectInic;i<sectFin+1;i++){
				auxiliar.SectorLogico = i;
				down(mutex2);
				agregarElemento_fscan(&pedAcum,auxiliar,'F');
				up(mutex2);
			}
		}

	}

	else {
		NIPC paquete3;
		char* bufferNIPC2;
		char mensaje[1024] = "ha ingresado un valor INVALIDO para el disco.";
		cargarEstructura(&paquete3,1,mensaje);
		bufferNIPC2 = malloc(sizeof(char)* paquete3.payloadLength + sizeof(uint8_t) + sizeof(uint16_t) );
		serializar_NIPC(paquete3,bufferNIPC2);
		enviarNIPC(bufferNIPC2,paquete3.payloadLength+3,aux.cliente);
		free(bufferNIPC2);
		free(paquete3.mensaje);
	}

}

void comandoTrace(parametros aux)
{
	if(strcmp(algoritmo,"LOOK") ==0)	trace_look(&pedInic,&pedFin,aux);
	if(strcmp(algoritmo,"F-SCAN") ==0) 	trace_fscan(&pedInic,&pedFin,aux);
}


void pedidoLectura(parametros aux)
{
	colaParam auxiliar;
	int elSector =0;
	memcpy(&elSector,aux.data,sizeof(uint32_t));

	auxiliar.SectorLogico = elSector;
	memset(auxiliar.buffer,0,513);
	auxiliar.codigo = 1;
	auxiliar.flag = 4;
	auxiliar.socket = aux.cliente;
	auxiliar.sem = -99;
	auxiliar.sgte = NULL;
	if(strcmp(algoritmo,"LOOK") ==0){
			selecCola_look(&pedInic,&pedFin,auxiliar,sentidoBrazo);
		}
	if(strcmp(algoritmo,"F-SCAN") ==0){
		down(mutex2);
		agregarElemento_fscan(&pedAcum,auxiliar,'F');
		up(mutex2);
	}
}

void pedidoLecturaD(parametros aux)
{
	colaParam auxiliar;
	char *strSectInic,*strSectFin;
	int sectInic, sectFin, i;
	char buffer[512];
	memset(buffer,0,512);

	strSectInic=strtok(aux.data,".");
	strSectFin=strtok(NULL,".");

	sectInic=atoi(strSectInic);
	sectFin=atoi(strSectFin);
	if(sectInic >= 0 && sectFin <= (sector*pista-1)){
		memset(auxiliar.buffer,0,512);
		auxiliar.codigo = 2;
		auxiliar.flag = 5;
		auxiliar.socket = aux.cliente;
		auxiliar.sem = -99;
		auxiliar.sgte = NULL;
		contadorDuplicado = 0;
		cantidadDuplicada = sectFin - sectInic;

		if(strcmp(algoritmo,"LOOK") ==0){
			for(i=sectInic;i<sectFin+1;i++){
				auxiliar.SectorLogico = i;
				selecCola_look(&pedInic,&pedFin,auxiliar,sentidoBrazo);
					}
			}

		if(strcmp(algoritmo,"F-SCAN") ==0) {
			for(i=sectInic;i<sectFin+1;i++){
				auxiliar.SectorLogico = i;
				down(mutex2);
				agregarElemento_fscan(&pedAcum,auxiliar,'F');
				up(mutex2);
			}
		}
	}
	else {
				NIPC paquete3;
				char* bufferNIPC2;
				char mensaje[1024] = "ha ingresado un valor INVALIDO para el disco en LEER.";
				cargarEstructura(&paquete3,1,mensaje);
				bufferNIPC2 = malloc(sizeof(char)* paquete3.payloadLength + sizeof(uint8_t) + sizeof(uint16_t) );
				serializar_NIPC(paquete3,bufferNIPC2);
				enviarNIPC(bufferNIPC2,paquete3.payloadLength+3,aux.cliente);
				free(bufferNIPC2);
				free(paquete3.mensaje);
	}
}

void pedidoEscritura(parametros aux)
{
	int elSector;
	colaParam auxiliar;

	memcpy(&elSector,aux.data+512,sizeof(uint32_t));

	auxiliar.SectorLogico = elSector;
	memcpy(auxiliar.buffer,aux.data,512);
	auxiliar.codigo = 3;
	auxiliar.flag = 2;
	auxiliar.socket = aux.cliente;
	auxiliar.sem = -99;
	auxiliar.sgte = NULL;

	if(strcmp(algoritmo,"LOOK") ==0){
				selecCola_look(&pedInic,&pedFin,auxiliar,sentidoBrazo);
			}

	if(strcmp(algoritmo,"F-SCAN") ==0) {
		down(mutex2);
		agregarElemento_fscan(&pedAcum,auxiliar,'F');
		up(mutex2);
	}
}

void pedidoEscrituraD(parametros aux)
{
	int elSector=0;
	colaParam auxiliar;

	memcpy(&elSector,aux.data+512,sizeof(uint32_t));

	auxiliar.SectorLogico = elSector;
	memset(auxiliar.buffer,0,512);
	memcpy(auxiliar.buffer,aux.data,512);
	auxiliar.codigo = 4;
	auxiliar.flag = 3;
	auxiliar.socket = aux.cliente;
	auxiliar.sem = -99;
	auxiliar.sgte = NULL;

	if(strcmp(algoritmo,"LOOK") ==0){
				selecCola_look(&pedInic,&pedFin,auxiliar,sentidoBrazo);
			}

	if(strcmp(algoritmo,"F-SCAN") ==0) {
		down(mutex2);
		agregarElemento_fscan(&pedAcum,auxiliar,'F');
		up(mutex2);
	}
}

/*
void agregarPed(ptrColaParam *ptrCabeza,ptrColaParam *ptrTalon,parametros elemento)
{
	ptrColaParam ptrNue;
    ptrNue = malloc(sizeof(colaParam));

    if (ptrNue!= NULL) {
    	ptrNue->param.cliente=elemento.cliente;
    	ptrNue->param.fd=elemento.fd;
    	ptrNue->param.id=elemento.id;
    	ptrNue->param.semaforo=elemento.semaforo;
    	ptrNue->param.data=malloc(512+sizeof(uint32_t));
        memcpy(ptrNue->param.data,elemento.data,512);

        ptrNue->sgte= NULL;

        if ((*ptrCabeza)==NULL){
            *ptrCabeza=ptrNue;
		}
        else{
            (*ptrTalon)->sgte=ptrNue;
        }
        *ptrTalon=ptrNue;
    }
    else{
        printf("error en malloc()\n");
    }
}

parametros quitarPed(ptrColaParam *ptrCabeza,ptrColaParam *ptrTalon)
{

	parametros valor;
    ptrColaParam ptrTemp;

    if(&ptrCabeza!=NULL){
    	valor.cliente=(*ptrCabeza)->param.cliente;
    	valor.fd=(*ptrCabeza)->param.fd;
    	valor.id=(*ptrCabeza)->param.id;
    	valor.semaforo=(*ptrCabeza)->param.semaforo;
    	valor.data=malloc(512);
    	memcpy(valor.data,(*ptrCabeza)->param.data,512);
    	ptrTemp=*ptrCabeza;
    	*ptrCabeza=(*ptrCabeza)->sgte;

    	if (*ptrCabeza== NULL){
    		*ptrTalon=NULL;
    	}

    	free(ptrTemp);
    }
    return valor;
}*/

void* eliminar(){
	while(1){
		if(strcmp(algoritmo,"LOOK") ==0){
			if(pedInic!=NULL || pedFin != NULL){
				calcularTiempos_look();
			}
		}
		if(strcmp(algoritmo,"F-SCAN") ==0){
			if(pedInic==NULL && pedFin == NULL){
				descargarAcumulada(&pedAcum);
			}
			if(pedInic!=NULL || pedFin != NULL){
				calcularTiempos_fscan();
			}
		}

	}
}

void* encolarEpoll()
{
	int aux2;
	int i=0;
	int client;
	int elFlag;
	char buffer[1024];
	struct sockaddr_un data;
	struct epoll_event *eventos;
	struct epoll_event evento;
	while(1)
	{
		eventos=malloc(MAX_EVENTOS*sizeof(struct epoll_event));

		aux2=epoll_wait(epoll,eventos,MAX_EVENTOS,-1);
		memset(buffer,0,1024);
		for(i=0;i<aux2;i++){
					if(eventos[i].data.fd == socketServer) {
						client = aceptarCliente(socketServer,&data,1);
						elFlag = fcntl(client, F_GETFL, 0);
						elFlag |= O_NONBLOCK;
						fcntl(client, F_SETFL, elFlag);
						evento.events = EPOLLIN | EPOLLET;
						evento.data.fd = client;
						epoll_ctl(epoll, EPOLL_CTL_ADD, evento.data.fd, &evento);
					}
					else if ((eventos[i].data.fd == sock)&&(invalid==0)){
							int cliente=0;
							cliente=aceptarClienteA(sock,data2);
							elFlag = fcntl(cliente, F_GETFL, 0);
							elFlag |= O_NONBLOCK;
							fcntl(cliente, F_SETFL, elFlag);
							evento.events = EPOLLIN | EPOLLET;
							evento.data.fd = cliente;
							epoll_ctl(epoll, EPOLL_CTL_ADD, evento.data.fd, &evento);
							//termina el ingreso del FS

						}
					else{
						int bytesRecibidos;
						memset(buffer,0,1024);
						bytesRecibidos = recibirDatos(buffer,eventos[i].data.fd);
						down(mutexColaEpoll);
						encolarColaEpoll(buffer,cliente);
						up(mutexColaEpoll);
						if(bytesRecibidos<0){
							close(eventos[i].data.fd);
							evento.events = EPOLLIN ;
							evento.data.fd = eventos[i].data.fd;
							epoll_ctl(epoll, EPOLL_CTL_DEL , evento.data.fd, &evento);
							printf("cerrar sock\n");

						}
					}
		}
		int ret;
		memset(buffer,0,519);
		down(mutexSock);
		ret=recv(sock,buffer,519,MSG_WAITALL);
		up(mutexSock);
		if(ret<=0){
			//printf("fallo recv\n");
		}

		else{
			down(mutexColaEpoll);
			encolarColaEpoll(buffer,sock);
			up(mutexColaEpoll);

		}
	//}
		//		}
	}
	return 0;
}

int encolarColaEpoll(char* buffer,int socket)
{
	ptrColaEpoll ptrNue;
	ptrNue = malloc(sizeof(colaEpoll));

	    if (ptrNue!= NULL) {
	        ptrNue->elSock=socket;
		memset(ptrNue->elBuff,0,519);
	        memcpy(ptrNue->elBuff,buffer,1024);
	        ptrNue->ptrSgte= NULL;

	        if (epollColaInic ==NULL){
	        	epollColaInic =ptrNue;
			}
	        else{
	        	epollColaFin->ptrSgte=ptrNue;
	        }

	        epollColaFin=ptrNue;
	    }
	    else{
	        printf("error en malloc()\n");
	    }
	return 0;
}

int quitarColaEpoll(char* buffer)
{
		int fd;
	    ptrColaEpoll ptrTemp;

	    fd=epollColaInic->elSock;
	    memset(buffer,0,519);
	    memcpy(buffer,epollColaInic->elBuff,1024);

	    ptrTemp=epollColaInic;
	    epollColaInic=epollColaInic->ptrSgte;

	    if (epollColaInic== NULL){
	        epollColaFin=NULL;
	    }

	    free(ptrTemp);

	return fd; //socket
}

void* atenderConsola()
{
	int resul;
	char buffer[100];
	while(1){
		resul=recv(socketConsola,buffer,100,0);
		if(resul<=0){
					//printf("fallo recv\n");
				}
		else{
			down(mutexColaEpoll);
			encolarColaEpoll(buffer,socketConsola);
			up(mutexColaEpoll);
		}
	}
}
